home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / FLTK-1.0.6 / src / Fl_x.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1999-05-06  |  24.8 KB  |  871 lines

  1. //
  2. // "$Id: Fl_x.cxx,v 1.24.2.6 1999/05/06 06:20:47 bill Exp $"
  3. //
  4. // X specific code for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-1999 by Bill Spitzak and others.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems to "fltk-bugs@easysw.com".
  24. //
  25.  
  26. #ifdef WIN32
  27. #include "Fl_win32.cxx"
  28. #else
  29.  
  30. #define CONSOLIDATE_MOTION 1
  31. /**** Define this if your keyboard lacks a backspace key... ****/
  32. /* #define BACKSPACE_HACK 1 */
  33.  
  34. #include <config.h>
  35. #include <FL/Fl.H>
  36. #include <FL/x.H>
  37. #include <FL/Fl_Window.H>
  38. #include <ctype.h>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <unistd.h>
  43. #include <sys/time.h>
  44.  
  45. ////////////////////////////////////////////////////////////////
  46. // interface to poll/select call:
  47.  
  48. #if HAVE_POLL
  49.  
  50. #include <poll.h>
  51. static pollfd *pollfds = 0;
  52.  
  53. #else
  54.  
  55. #if HAVE_SYS_SELECT_H
  56. #  include <sys/select.h>
  57. #endif /* HAVE_SYS_SELECT_H */
  58.  
  59. // The following #define is only needed for HP-UX 9.x and earlier:
  60. //#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
  61.  
  62. static fd_set fdsets[3];
  63. static int maxfd;
  64. #define POLLIN 1
  65. #define POLLOUT 4
  66. #define POLLERR 8
  67.  
  68. #endif /* HAVE_POLL */
  69.  
  70. static int nfds = 0;
  71. static int fd_array_size = 0;
  72. static struct FD {
  73.   int fd;
  74.   short events;
  75.   void (*cb)(int, void*);
  76.   void* arg;
  77. } *fd = 0;
  78.  
  79. void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
  80.   remove_fd(n,events);
  81.   int i = nfds++;
  82.   if (i >= fd_array_size) {
  83.     fd_array_size = 2*fd_array_size+1;
  84.     fd = (FD*)realloc(fd, fd_array_size*sizeof(FD));
  85. #if HAVE_POLL
  86.     pollfds = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
  87. #endif
  88.   }
  89.   fd[i].fd = n;
  90.   fd[i].events = events;
  91.   fd[i].cb = cb;
  92.   fd[i].arg = v;
  93. #if HAVE_POLL
  94.   fds[i].fd = n;
  95.   fds[i].events = events;
  96. #else
  97.   if (events & POLLIN) FD_SET(n, &fdsets[0]);
  98.   if (events & POLLOUT) FD_SET(n, &fdsets[1]);
  99.   if (events & POLLERR) FD_SET(n, &fdsets[2]);
  100.   if (n > maxfd) maxfd = n;
  101. #endif
  102. }
  103.  
  104. void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
  105.   Fl::add_fd(fd, POLLIN, cb, v);
  106. }
  107.  
  108. void Fl::remove_fd(int n, int events) {
  109.   int i,j;
  110.   for (i=j=0; i<nfds; i++) {
  111.     if (fd[i].fd == n) {
  112.       int e = fd[i].events & ~events;
  113.       if (!e) continue; // if no events left, delete this fd
  114.       fd[i].events = e;
  115. #if HAVE_POLL
  116.       fds[j].events = e;
  117. #endif
  118.     }
  119.     // move it down in the array if necessary:
  120.     if (j<i) {
  121.       fd[j]=fd[i];
  122. #if HAVE_POLL
  123.       fds[j]=fds[i];
  124. #endif
  125.     }
  126.     j++;
  127.   }
  128.   nfds = j;
  129. #if !HAVE_POLL
  130.   if (events & POLLIN) FD_CLR(n, &fdsets[0]);
  131.   if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
  132.   if (events & POLLERR) FD_CLR(n, &fdsets[2]);
  133.   if (n == maxfd) maxfd--;
  134. #endif
  135. }
  136.  
  137. void Fl::remove_fd(int n) {
  138.   remove_fd(n, -1);
  139. }
  140.  
  141. int fl_ready() {
  142.   if (XQLength(fl_display)) return 1;
  143. #if HAVE_POLL
  144.   return ::poll(fds, nfds, 0);
  145. #else
  146.   timeval t;
  147.   t.tv_sec = 0;
  148.   t.tv_usec = 0;
  149.   fd_set fdt[3];
  150.   fdt[0] = fdsets[0];
  151.   fdt[1] = fdsets[1];
  152.   fdt[2] = fdsets[2];
  153.   return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
  154. #endif
  155. }
  156.  
  157. #if CONSOLIDATE_MOTION
  158. static Fl_Window* send_motion;
  159. extern Fl_Window* fl_xmousewin;
  160. #endif
  161. static void do_queued_events() {
  162.  while (XEventsQueued(fl_display,QueuedAfterReading)) {
  163.     XEvent xevent;
  164.     XNextEvent(fl_display, &xevent);
  165.     fl_handle(xevent);
  166.   }
  167. #if CONSOLIDATE_MOTION
  168.   if (send_motion && send_motion == fl_xmousewin) {
  169.     send_motion = 0;
  170.     Fl::handle(FL_MOVE, fl_xmousewin);
  171.   }
  172. #endif
  173. }
  174.  
  175. double fl_wait(int timeout_flag, double time) {
  176.  
  177.   // OpenGL and other broken libraries call XEventsQueued
  178.   // unnecessarily and thus cause the file descriptor to not be ready,
  179.   // so we must check for already-read events:
  180.   if (XQLength(fl_display)) {do_queued_events(); return time;}
  181.  
  182. #if !HAVE_POLL
  183.   fd_set fdt[3];
  184.   fdt[0] = fdsets[0];
  185.   fdt[1] = fdsets[1];
  186.   fdt[2] = fdsets[2];
  187. #endif
  188.   int n;
  189.  
  190.   if (!timeout_flag) {
  191. #if HAVE_POLL
  192.     n = ::poll(fds, nfds, -1);
  193. #else
  194.     n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
  195. #endif
  196.   } else {
  197. #if HAVE_POLL
  198.     int n = ::poll(fds, nfds, time > 0.0 ? int(time*1000) : 0);
  199. #else
  200.     timeval t;
  201.     if (time <= 0.0) {
  202.       t.tv_sec = 0;
  203.       t.tv_usec = 0;
  204.     } else {
  205.       t.tv_sec = int(time);
  206.       t.tv_usec = int(1000000 * (time-t.tv_sec));
  207.     }
  208.     n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
  209. #endif
  210.   }
  211.   if (n > 0) {
  212.     for (int i=0; i<nfds; i++) {
  213. #if HAVE_POLL
  214.       if (fds[i].revents) fd[i].cb(fd[i].fd, fd[i].arg);
  215. #else
  216.       int f = fd[i].fd;
  217.       short revents = 0;
  218.       if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
  219.       if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
  220.       if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
  221.       if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
  222. #endif
  223.     }
  224.   }
  225.   return time;
  226. }
  227.  
  228. ////////////////////////////////////////////////////////////////
  229.  
  230. Display *fl_display;
  231. int fl_screen;
  232. XVisualInfo *fl_visual;
  233. Colormap fl_colormap;
  234.  
  235. static Atom wm_delete_window;
  236. static Atom wm_protocols;
  237. static Atom _motif_wm_hints;
  238.  
  239. static void fd_callback(int,void *) {do_queued_events();}
  240.  
  241. static int io_error_handler(Display*) {Fl::fatal("X I/O error"); return 0;}
  242.  
  243. static int xerror_handler(Display* d, XErrorEvent* e) {
  244.   char buf1[128], buf2[128];
  245.   sprintf(buf1, "XRequest.%d", e->request_code);
  246.   XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128);
  247.   XGetErrorText(d, e->error_code, buf1, 128);
  248.   Fl::warning("%s: %s 0x%lx", buf2, buf1, e->resourceid);
  249.   return 0;
  250. }
  251.  
  252. void fl_open_display() {
  253.   if (fl_display) return;
  254.  
  255.   XSetIOErrorHandler(io_error_handler);
  256.   XSetErrorHandler(xerror_handler);
  257.  
  258.   Display *d = XOpenDisplay(0);
  259.   if (!d) Fl::fatal("Can't open display: %s",XDisplayName(0));
  260.  
  261.   fl_display = d;
  262.  
  263.   wm_delete_window = XInternAtom(d,"WM_DELETE_WINDOW",0);
  264.   wm_protocols = XInternAtom(d,"WM_PROTOCOLS",0);
  265.   _motif_wm_hints = XInternAtom(d,"_MOTIF_WM_HINTS",0);
  266.   Fl::add_fd(ConnectionNumber(d), POLLIN, fd_callback);
  267.  
  268.   fl_screen = DefaultScreen(fl_display);
  269. // construct an XVisualInfo that matches the default Visual:
  270.   XVisualInfo templt; int num;
  271.   templt.visualid = XVisualIDFromVisual(DefaultVisual(fl_display,fl_screen));
  272.   fl_visual = XGetVisualInfo(fl_display, VisualIDMask, &templt, &num);
  273.   fl_colormap = DefaultColormap(fl_display,fl_screen);
  274. }
  275.  
  276. void fl_close_display() {
  277.   Fl::remove_fd(ConnectionNumber(fl_display));
  278.   XCloseDisplay(fl_display);
  279. }
  280.  
  281. int Fl::h() {
  282.   fl_open_display();
  283.   return DisplayHeight(fl_display,fl_screen);
  284. }
  285.  
  286. int Fl::w() {
  287.   fl_open_display();
  288.   return DisplayWidth(fl_display,fl_screen);
  289. }
  290.  
  291. void Fl::get_mouse(int &x, int &y) {
  292.   fl_open_display();
  293.   Window root = RootWindow(fl_display, fl_screen);
  294.   Window c; int mx,my,cx,cy; unsigned int mask;
  295.   XQueryPointer(fl_display,root,&root,&c,&mx,&my,&cx,&cy,&mask);
  296.   x = mx;
  297.   y = my;
  298. }
  299.  
  300. ////////////////////////////////////////////////////////////////
  301.  
  302. const XEvent* fl_xevent; // the current x event
  303. ulong fl_event_time; // the last timestamp from an x event
  304.  
  305. char fl_key_vector[32]; // used by Fl::get_key()
  306.  
  307. // Record event mouse position and state from an XEvent:
  308.  
  309. static int px, py;
  310. static ulong ptime;
  311.  
  312. static void set_event_xy() {
  313. #if CONSOLIDATE_MOTION
  314.   send_motion = 0;
  315. #endif
  316.   Fl::e_x_root = fl_xevent->xbutton.x_root;
  317.   Fl::e_x = fl_xevent->xbutton.x;
  318.   Fl::e_y_root = fl_xevent->xbutton.y_root;
  319.   Fl::e_y = fl_xevent->xbutton.y;
  320.   Fl::e_state = fl_xevent->xbutton.state << 16;
  321.   fl_event_time = fl_xevent->xbutton.time;
  322. #ifdef __sgi
  323.   // get the meta key off PC keyboards:
  324.   if (fl_key_vector[18]&0x18) Fl::e_state |= FL_META;
  325. #endif
  326.   // turn off is_click if enough time or mouse movement has passed:
  327.   if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 
  328.       || fl_event_time >= ptime+1000)
  329.     Fl::e_is_click = 0;
  330. }
  331.  
  332. // if this is same event as last && is_click, increment click count:
  333. static inline void checkdouble() {
  334.   if (Fl::e_is_click == Fl::e_keysym)
  335.     Fl::e_clicks++;
  336.   else {
  337.     Fl::e_clicks = 0;
  338.     Fl::e_is_click = Fl::e_keysym;
  339.   }
  340.   px = Fl::e_x_root;
  341.   py = Fl::e_y_root;
  342.   ptime = fl_event_time;
  343. }
  344.  
  345. static Fl_Window* resize_bug_fix;
  346.  
  347. ////////////////////////////////////////////////////////////////
  348.  
  349. int fl_handle(const XEvent& xevent)
  350. {
  351.   fl_xevent = &xevent;
  352.   Window xid = xevent.xany.window;
  353.  
  354.   switch (xevent.type) {
  355.  
  356.   // events where we don't care about window:
  357.  
  358.   case KeymapNotify:
  359.     memcpy(fl_key_vector, xevent.xkeymap.key_vector, 32);
  360.     return 0;
  361.  
  362.   case MappingNotify:
  363.     XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
  364.     return 0;
  365.  
  366.   // events where interesting window id is in a different place:
  367.   case CirculateNotify:
  368.   case CirculateRequest:
  369.   case ConfigureNotify:
  370.   case ConfigureRequest:
  371.   case CreateNotify:
  372.   case DestroyNotify:
  373.   case GravityNotify:
  374.   case MapNotify:
  375.   case MapRequest:
  376.   case ReparentNotify:
  377.   case UnmapNotify:
  378.     xid = xevent.xmaprequest.window;
  379.     break;
  380.   }
  381.  
  382.   int event = 0;
  383.   Fl_Window* window = fl_find(xid);
  384.  
  385.   if (window) switch (xevent.type) {
  386.  
  387.   case ClientMessage:
  388.     if ((Atom)(xevent.xclient.data.l[0]) == wm_delete_window) event = FL_CLOSE;
  389.     break;
  390.  
  391.   case MapNotify:
  392.     event = FL_SHOW;
  393.     break;
  394.  
  395.   case UnmapNotify:
  396.     event = FL_HIDE;
  397.     break;
  398.  
  399.   case Expose:
  400.     Fl_X::i(window)->wait_for_expose = 0;
  401. #if 0
  402.     // try to keep windows on top even if WM_TRANSIENT_FOR does not work:
  403.     // opaque move/resize window managers do not like this, so I disabled it.
  404.     if (Fl::first_window()->non_modal() && window != Fl::first_window())
  405.       Fl::first_window()->show();
  406. #endif
  407.  
  408.   case GraphicsExpose:
  409.     window->damage(FL_DAMAGE_EXPOSE, xevent.xexpose.x, xevent.xexpose.y,
  410.            xevent.xexpose.width, xevent.xexpose.height);
  411.     return 1;
  412.  
  413.   case ButtonPress:
  414.     Fl::e_keysym = FL_Button + xevent.xbutton.button;
  415.     set_event_xy(); checkdouble();
  416.     Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
  417.     event = FL_PUSH;
  418.     break;
  419.  
  420.   case MotionNotify:
  421.     set_event_xy();
  422. #if CONSOLIDATE_MOTION
  423.     send_motion = fl_xmousewin = window;
  424.     return 0;
  425. #else
  426.     event = FL_MOVE;
  427.     break;
  428. #endif
  429.  
  430.   case ButtonRelease:
  431.     Fl::e_keysym = FL_Button + xevent.xbutton.button;
  432.     set_event_xy();
  433.     Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
  434.     event = FL_RELEASE;
  435.     break;
  436.  
  437.   case FocusIn:
  438.     event = FL_FOCUS;
  439.     break;
  440.  
  441.   case FocusOut:
  442.     event = FL_UNFOCUS;
  443.     break;
  444.  
  445.   case KeyPress: {
  446.     int keycode = xevent.xkey.keycode;
  447.     fl_key_vector[keycode/8] |= (1 << (keycode%8));
  448.     static char buffer[21];
  449.     KeySym keysym;
  450.     int len = XLookupString((XKeyEvent*)&(xevent.xkey),buffer,20,&keysym,0);
  451.     if (keysym && keysym < 0x400) { // a character in latin-1,2,3,4 sets
  452.       // force it to type a character (not sure if this ever is needed):
  453.       if (!len) {buffer[0] = char(keysym); len = 1;}
  454.       // ignore all effects of shift on the keysyms, which makes it a lot
  455.       // easier to program shortcuts and is Windoze-compatable:
  456.       keysym = XKeycodeToKeysym(fl_display, keycode, 0);
  457.     }
  458. #ifdef __sgi
  459.     // You can plug a microsoft keyboard into an sgi but the extra shift
  460.     // keys are not translated.  Make them translate like XFree86 does:
  461.     if (!keysym) switch(keycode) {
  462.     case 147: keysym = FL_Meta_L; break;
  463.     case 148: keysym = FL_Meta_R; break;
  464.     case 149: keysym = FL_Menu; break;
  465.     }
  466. #endif
  467. #if BACKSPACE_HACK
  468.     // Attempt to fix keyboards that send "delete" for the key in the
  469.     // upper-right corner of the main keyboard.  But it appears that
  470.     // very few of these remain?
  471.     static int got_backspace;
  472.     if (!got_backspace) {
  473.       if (keysym == FL_Delete) keysym = FL_BackSpace;
  474.       else if (keysym == FL_BackSpace) got_backspace = 1;
  475.     }
  476. #endif
  477.     // We have to get rid of the XK_KP_function keys, because they are
  478.     // not produced on Windoze and thus case statements tend not to check
  479.     // for them.  There are 15 of these in the range 0xff91 ... 0xff9f
  480.     if (keysym >= 0xff91 && keysym <= 0xff9f) {
  481.       // Try to make them turn into FL_KP+'c' so that NumLock is
  482.       // irrelevant, by looking at the shifted code.  This matches the
  483.       // behavior of the translator in Fl_win32.C, and IMHO is the
  484.       // user-friendly result:
  485.       unsigned long keysym1 = XKeycodeToKeysym(fl_display, keycode, 1);
  486.       if (keysym1 <= 0x7f || keysym1 > 0xff9f && keysym1 <= FL_KP_Last) {
  487.     keysym = keysym1 | FL_KP;
  488.     buffer[0] = char(keysym1) & 0x7F;
  489.     len = 1;
  490.       } else {
  491.     // If that failed to work, just translate them to the matching
  492.     // normal function keys:
  493.     static const unsigned short table[15] = {
  494.       FL_F+1, FL_F+2, FL_F+3, FL_F+4,
  495.       FL_Home, FL_Left, FL_Up, FL_Right,
  496.       FL_Down, FL_Page_Up, FL_Page_Down, FL_End,
  497.       0xff0b/*XK_Clear*/, FL_Insert, FL_Delete};
  498.     keysym = table[keysym-0xff91];
  499.       }
  500.     }
  501.     buffer[len] = 0;
  502.     Fl::e_keysym = int(keysym);
  503.     Fl::e_text = buffer;
  504.     Fl::e_length = len;
  505.     set_event_xy(); Fl::e_is_click = 0;
  506.     if (Fl::event_state(FL_CTRL) && keysym == '-') buffer[0] = 0x1f; // ^_
  507.     event = FL_KEYBOARD;
  508.     break;}
  509.  
  510.   case KeyRelease: {
  511.     int keycode = xevent.xkey.keycode;
  512.     fl_key_vector[keycode/8] &= ~(1 << (keycode%8));
  513.     set_event_xy();}
  514.     break;
  515.  
  516.   case EnterNotify:
  517.     if (xevent.xcrossing.detail == NotifyInferior) break;
  518.     // XInstallColormap(fl_display, Fl_X::i(window)->colormap);
  519.     set_event_xy();
  520.     Fl::e_state = xevent.xcrossing.state << 16;
  521.     event = FL_ENTER;
  522.     break;
  523.  
  524.   case LeaveNotify:
  525.     if (xevent.xcrossing.detail == NotifyInferior) break;
  526.     set_event_xy();
  527.     Fl::e_state = xevent.xcrossing.state << 16;
  528.     event = FL_LEAVE;
  529.     break;
  530.  
  531.   case ConfigureNotify: {
  532.     // We cannot rely on the x,y position in the configure notify event.
  533.     // I now think this is an unavoidable problem with X: it is impossible
  534.     // for a window manager to prevent the "real" notify event from being
  535.     // sent when it resizes the contents, even though it can send an
  536.     // artificial event with the correct position afterwards (and some
  537.     // window managers do not send this fake event anyway)
  538.     // So anyway, do a round trip to find the correct x,y:
  539.     Window r, c; int X, Y, wX, wY; unsigned int m;
  540.     XQueryPointer(fl_display, fl_xid(window), &r, &c, &X, &Y, &wX, &wY, &m);
  541.     resize_bug_fix = window;
  542.     window->resize(X-wX, Y-wY,
  543.            xevent.xconfigure.width, xevent.xconfigure.height);
  544.     return 1;}
  545.   }
  546.  
  547.   return Fl::handle(event, window);
  548. }
  549.  
  550. ////////////////////////////////////////////////////////////////
  551.  
  552. void Fl_Window::resize(int X,int Y,int W,int H) {
  553.   int is_a_resize = (W != w() || H != h());
  554.   int resize_from_program = (this != resize_bug_fix);
  555.   if (!resize_from_program) resize_bug_fix = 0;
  556.   if (X != x() || Y != y()) set_flag(FL_FORCE_POSITION);
  557.   else if (!is_a_resize) return;
  558.   if (is_a_resize) {
  559.     Fl_Group::resize(X,Y,W,H);
  560.     if (shown()) {redraw(); i->wait_for_expose = 1;}
  561.   } else {
  562.     x(X); y(Y);
  563.   }
  564.   if (resize_from_program && shown()) {
  565.     if (is_a_resize)
  566.       XMoveResizeWindow(fl_display, i->xid, X, Y, W>0 ? W : 1, H>0 ? H : 1);
  567.     else
  568.       XMoveWindow(fl_display, i->xid, X, Y);
  569.   }
  570. }
  571.  
  572. ////////////////////////////////////////////////////////////////
  573.  
  574. // A subclass of Fl_Window may call this to associate an X window it
  575. // creates with the Fl_Window:
  576.  
  577. void fl_fix_focus(); // in Fl.cxx
  578.  
  579. Fl_X* Fl_X::set_xid(Fl_Window* w, Window xid) {
  580.   Fl_X* x = new Fl_X;
  581.   x->xid = xid;
  582.   x->other_xid = 0;
  583.   x->setwindow(w);
  584.   x->next = Fl_X::first;
  585.   x->region = 0;
  586.   x->wait_for_expose = 1;
  587.   Fl_X::first = x;
  588.   if (w->modal()) {Fl::modal_ = w; fl_fix_focus();}
  589.   return x;
  590. }
  591.  
  592. // More commonly a subclass calls this, because it hides the really
  593. // ugly parts of X and sets all the stuff for a window that is set
  594. // normally.  The global variables like fl_show_iconic are so that
  595. // subclasses of *that* class may change the behavior...
  596.  
  597. char fl_show_iconic;    // hack for iconize()
  598. int fl_background_pixel = -1; // hack to speed up bg box drawing
  599. int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
  600.  
  601. static const int childEventMask = ExposureMask;
  602.  
  603. static const int XEventMask =
  604. ExposureMask|StructureNotifyMask
  605. |KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask
  606. |ButtonPressMask|ButtonReleaseMask
  607. |EnterWindowMask|LeaveWindowMask
  608. |PointerMotionMask;
  609.  
  610. void Fl_X::make_xid(Fl_Window* w, XVisualInfo *visual, Colormap colormap)
  611. {
  612.   Fl_Group::current(0); // get rid of very common user bug: forgot end()
  613.  
  614.   int X = w->x();
  615.   int Y = w->y();
  616.   int W = w->w();
  617.   if (W <= 0) W = 1; // X don't like zero...
  618.   int H = w->h();
  619.   if (H <= 0) H = 1; // X don't like zero...
  620.   if (!w->parent() && !Fl::grab()) {
  621.     // force the window to be on-screen.  Usually the X window manager
  622.     // does this, but a few don't, so we do it here for consistency:
  623.     if (w->border()) {
  624.       // ensure border is on screen:
  625.       // (assumme extremely minimal dimensions for this border)
  626.       const int top = 20;
  627.       const int left = 1;
  628.       const int right = 1;
  629.       const int bottom = 1;
  630.       if (X+W+right > Fl::w()) X = Fl::w()-right-W;
  631.       if (X-left < 0) X = left;
  632.       if (Y+H+bottom > Fl::h()) Y = Fl::h()-bottom-H;
  633.       if (Y-top < 0) Y = top;
  634.     }
  635.     // now insure contents are on-screen (more important than border):
  636.     if (X+W > Fl::w()) X = Fl::w()-W;
  637.     if (X < 0) X = 0;
  638.     if (Y+H > Fl::h()) Y = Fl::h()-H;
  639.     if (Y < 0) Y = 0;
  640.   }
  641.  
  642.   ulong root = w->parent() ?
  643.     fl_xid(w->window()) : RootWindow(fl_display, fl_screen);
  644.  
  645.   XSetWindowAttributes attr;
  646.   int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
  647.   attr.event_mask = w->parent() ? childEventMask : XEventMask;
  648.   attr.colormap = colormap;
  649.   attr.border_pixel = 0;
  650.   attr.bit_gravity = 0; // StaticGravity;
  651.   attr.override_redirect = 0;
  652.   if (Fl::grab()) {
  653.     attr.save_under = 1; mask |= CWSaveUnder;
  654.     if (!w->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;}
  655.   }
  656.   if (fl_background_pixel >= 0) {
  657.     attr.background_pixel = fl_background_pixel;
  658.     fl_background_pixel = -1;
  659.     mask |= CWBackPixel;
  660.   }
  661.  
  662.   Fl_X* x =
  663.     set_xid(w, XCreateWindow(fl_display,
  664.                  root,
  665.                  X, Y, W, H,
  666.                  0, // borderwidth
  667.                  visual->depth,
  668.                  InputOutput,
  669.                  visual->visual,
  670.                  mask, &attr));
  671.   w->set_visible();
  672.   w->handle(FL_SHOW); // get child windows to appear
  673.   w->redraw();
  674.  
  675.   if (!w->parent() && !attr.override_redirect) {
  676.     // Communicate all kinds 'o junk to the X Window Manager:
  677.  
  678.     w->label(w->label(), w->iconlabel());
  679.  
  680.     XChangeProperty(fl_display, x->xid, wm_protocols,
  681.              XA_ATOM, 32, 0, (uchar*)&wm_delete_window, 1);
  682.  
  683.     // send size limits and border:
  684.     x->sendxjunk();
  685.  
  686.     // set the class property, which controls the icon used:
  687.     if (w->xclass()) {
  688.       char buffer[1024];
  689.       char *p; const char *q;
  690.       // truncate on any punctuation, because they break XResource lookup:
  691.       for (p = buffer, q = w->xclass(); isalnum(*q)||(*q&128);) *p++ = *q++;
  692.       *p++ = 0;
  693.       // create the capitalized version:
  694.       q = buffer;
  695.       *p = toupper(*q++); if (*p++ == 'X') *p++ = toupper(*q++);
  696.       while ((*p++ = *q++));
  697.       XChangeProperty(fl_display, x->xid, XA_WM_CLASS, XA_STRING, 8, 0,
  698.               (unsigned char *)buffer, p-buffer-1);
  699.     }
  700.  
  701.     if (w->non_modal() && x->next && !fl_disable_transient_for) {
  702.       // find some other window to be "transient for":
  703.       Fl_Window* w = x->next->w;
  704.       while (w->parent()) w = w->window();
  705.       XSetTransientForHint(fl_display, x->xid, fl_xid(w));
  706.     }
  707.  
  708.     XWMHints hints;
  709.     hints.flags = 0;
  710.     if (fl_show_iconic) {
  711.       hints.flags = StateHint;
  712.       hints.initial_state = IconicState;
  713.       fl_show_iconic = 0;
  714.     }
  715.     if (w->icon()) {
  716.       hints.icon_pixmap = (Pixmap)w->icon();
  717.       hints.flags       |= IconPixmapHint;
  718.     }
  719.     if (hints.flags) XSetWMHints(fl_display, x->xid, &hints);
  720.   }
  721.  
  722.   XMapWindow(fl_display, x->xid);
  723. }
  724.  
  725. ////////////////////////////////////////////////////////////////
  726. // Send X window stuff that can be changed over time:
  727.  
  728. void Fl_X::sendxjunk() {
  729.   if (w->parent()) return; // it's not a window manager window!
  730.  
  731.   if (!w->size_range_set) { // default size_range based on resizable():
  732.     if (w->resizable()) {
  733.       Fl_Widget *o = w->resizable();
  734.       int minw = o->w(); if (minw > 100) minw = 100;
  735.       int minh = o->h(); if (minh > 100) minh = 100;
  736.       w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
  737.     } else {
  738.       w->size_range(w->w(), w->h(), w->w(), w->h());
  739.     }
  740.     return; // because this recursively called here
  741.   }
  742.  
  743.   XSizeHints hints;
  744.   // memset(&hints, 0, sizeof(hints)); jreiser suggestion to fix purify?
  745.   hints.min_width = w->minw;
  746.   hints.min_height = w->minh;
  747.   hints.max_width = w->maxw;
  748.   hints.max_height = w->maxh;
  749.   hints.width_inc = w->dw;
  750.   hints.height_inc = w->dh;
  751.  
  752.   // see the file /usr/include/X11/Xm/MwmUtil.h:
  753.   // fill all fields to avoid bugs in kwm and perhaps other window managers:
  754.   // 0, MWM_FUNC_ALL, MWM_DECOR_ALL
  755.   long prop[5] = {0, 1, 1, 0, 0};
  756.  
  757.   if (hints.min_width != hints.max_width ||
  758.       hints.min_height != hints.max_height) { // resizable
  759.     hints.flags = PMinSize;
  760.     if (hints.max_width >= hints.min_width ||
  761.     hints.max_height >= hints.min_height) {
  762.       hints.flags = PMinSize|PMaxSize;
  763.       // unfortunately we can't set just one maximum size.  Guess a
  764.       // value for the other one.  Some window managers will make the
  765.       // window fit on screen when maximized, others will put it off screen:
  766.       if (hints.max_width < hints.min_width) hints.max_width = Fl::w();
  767.       if (hints.max_height < hints.min_height) hints.max_height = Fl::h();
  768.     }
  769.     if (hints.width_inc && hints.height_inc) hints.flags |= PResizeInc;
  770.     if (w->aspect) {
  771.       // stupid X!  It could insist that the corner go on the
  772.       // straight line between min and max...
  773.       hints.min_aspect.x = hints.max_aspect.x = hints.min_width;
  774.       hints.min_aspect.y = hints.max_aspect.y = hints.min_height;
  775.       hints.flags |= PAspect;
  776.     }
  777.   } else { // not resizable:
  778.     hints.flags = PMinSize|PMaxSize;
  779.     prop[0] = 1; // MWM_HINTS_FUNCTIONS
  780.     prop[1] = 1|2|16; // MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE
  781.   }
  782.  
  783.   if (w->flags() & Fl_Window::FL_FORCE_POSITION) {
  784.     hints.flags |= USPosition;
  785.     hints.x = w->x();
  786.     hints.y = w->y();
  787.   }
  788.  
  789.   if (!w->border()) {
  790.     prop[0] |= 2; // MWM_HINTS_DECORATIONS
  791.     prop[2] = 0; // no decorations
  792.   }
  793.  
  794.   XSetWMNormalHints(fl_display, xid, &hints);
  795.   XChangeProperty(fl_display, xid,
  796.           _motif_wm_hints, _motif_wm_hints,
  797.           32, 0, (unsigned char *)prop, 5);
  798. }
  799.  
  800. void Fl_Window::size_range_() {
  801.   size_range_set = 1;
  802.   if (shown()) i->sendxjunk();
  803. }
  804.  
  805. ////////////////////////////////////////////////////////////////
  806.  
  807. // returns pointer to the filename, or null if name ends with '/'
  808. const char *filename_name(const char *name) {
  809.   const char *p,*q;
  810.   for (p=q=name; *p;) if (*p++ == '/') q = p;
  811.   return q;
  812. }
  813.  
  814. void Fl_Window::label(const char *name,const char *iname) {
  815.   Fl_Widget::label(name);
  816.   iconlabel_ = iname;
  817.   if (shown() && !parent()) {
  818.     if (!name) name = "";
  819.     XChangeProperty(fl_display, i->xid, XA_WM_NAME,
  820.             XA_STRING, 8, 0, (uchar*)name, strlen(name));
  821.     if (!iname) iname = filename_name(name);
  822.     XChangeProperty(fl_display, i->xid, XA_WM_ICON_NAME, 
  823.             XA_STRING, 8, 0, (uchar*)iname, strlen(iname));
  824.   }
  825. }
  826.  
  827. ////////////////////////////////////////////////////////////////
  828. // Implement the virtual functions for the base Fl_Window class:
  829.  
  830. // If the box is a filled rectangle, we can make the redisplay *look*
  831. // faster by using X's background pixel erasing.  We can make it
  832. // actually *be* faster by drawing the frame only, this is done by
  833. // setting fl_boxcheat, which is seen by code in fl_drawbox.C:
  834. //
  835. // On XFree86 (and prehaps all X's) this has a problem if the window
  836. // is resized while a save-behind window is atop it.  The previous
  837. // contents are restored to the area, but this assummes the area
  838. // is cleared to background color.  So this is disabled in this version.
  839. // Fl_Window *fl_boxcheat;
  840. static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);}
  841.  
  842. void Fl_Window::show() {
  843.   if (!shown()) {
  844.     fl_open_display();
  845.     if (can_boxcheat(box())) fl_background_pixel = int(fl_xpixel(color()));
  846.     Fl_X::make_xid(this);
  847.   } else {
  848.     XMapRaised(fl_display, i->xid);
  849.   }
  850. }
  851.  
  852. Window fl_window;
  853. Fl_Window *Fl_Window::current_;
  854. GC fl_gc;
  855.  
  856. // make X drawing go into this window (called by subclass flush() impl.)
  857. void Fl_Window::make_current() {
  858.   static GC gc;    // the GC used by all X windows
  859.   if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0);
  860.   fl_window = i->xid;
  861.   fl_gc = gc;
  862.   current_ = this;
  863.   fl_clip_region(0);
  864. }
  865.  
  866. #endif
  867.  
  868. //
  869. // End of "$Id: Fl_x.cxx,v 1.24.2.6 1999/05/06 06:20:47 bill Exp $".
  870. //
  871.